/**
 * Copyright (c) 2008, Keisuke Inoue, Syracuse Unviersity
 * All rights reserved.
 *
 */
package edu.syr.iis.web.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

import org.hisrc.jscm.codemodel.lang.StringEscapeUtils;

import edu.syr.iis.web.model.Assignment;
import edu.syr.iis.web.model.AssignmentManager;
import edu.syr.iis.web.model.DialogueAct;
import edu.syr.iis.web.model.DialogueActDimension;
import edu.syr.iis.web.model.DialogueActDimensionManager;
import edu.syr.iis.web.model.DialogueActLabel;
import edu.syr.iis.web.model.DialogueActLabelManager;
import edu.syr.iis.web.model.DialogueActManager;
import edu.syr.iis.web.model.Message;
import edu.syr.iis.web.model.User;

/**
 * @author kinoue
 *
 */
public class DataExporter
{
    static final int FUNCTION = 1; 
    static final int DOMAIN_PART = 2;
    static final int DOMAIN_FULL = 4;

    static final int PREV_FUNCTION = 1;
    static final int NEXT_FUNCTION = 2;
    static final int PREV_DOMAIN_PART = 4;
    static final int NEXT_DOMAIN_PART = 8;
    static final int PREV_DOMAIN_FULL = 16;
    static final int NEXT_DOMAIN_FULL = 32;
    
    static String filter(String content)
    {
	String newContent = content.replaceAll("[\\[\\]]", "").replace('\"', ' ');
	newContent = newContent.replaceAll("http:\\/\\/[^\\s]+", "##URL_STRING##");
	newContent = newContent.replaceAll("www\\.[^\\s]+", "##URL_STRING##");
	newContent = newContent.replaceAll("[\\w\\-\\/_]+\\.[\\w\\-\\/_]+\\.[\\w\\-\\/_]+[^\\s]+","##URL_STRING##");
	newContent = newContent.replaceAll("[^ ]+((\\.html)|(\\.php)|(\\.asp)|(\\.jpg)|(\\.gif))", "##URL_STRING##");
	
	newContent = newContent.replaceAll("(^|\\D)\\d{5}+(\\D|$)", "$1##LONG_NUMBER##$2");
	newContent = newContent.replaceAll("(^|\\D)\\d{4}(\\D|$)", "$1##FOUR_DIGIT_NUMBER##$2");
	newContent = newContent.replaceAll("(^|\\D)\\d{3}(\\D|$)", "$1##THREE_DIGIT_NUMBER##$2");
	newContent = newContent.replaceAll("(^|\\D)\\d{2}(\\D|$)", "$1##TWO_DIGIT_NUMBER##$2");
	newContent = newContent.replaceAll("(^|\\D)\\d(\\D|$)", "$1##ONE_DIGIT_NUMBER##$2");

	newContent = newContent.replaceAll("(^|\\D)\\d{5}+(\\D|$)", "$1##LONG_NUMBER##$2");
	newContent = newContent.replaceAll("(^|\\D)\\d{4}(\\D|$)", "$1##FOUR_DIGIT_NUMBER##$2");
	newContent = newContent.replaceAll("(^|\\D)\\d{3}(\\D|$)", "$1##THREE_DIGIT_NUMBER##$2");
	newContent = newContent.replaceAll("(^|\\D)\\d{2}(\\D|$)", "$1##TWO_DIGIT_NUMBER##$2");
	newContent = newContent.replaceAll("(^|\\D)\\d(\\D|$)", "$1##ONE_DIGIT_NUMBER##$2");

	
	/*
	  newContent = newContent.replaceAll("(\\s|^|-|\\-|\\(|\\))\\d{5}+(\\s|$|\\-|-|\\(|\\))", "$1##LONG_NUMBER##$2");
	newContent = newContent.replaceAll("(\\s|^|-|\\-|\\(|\\))\\d{4}(\\s|$|\\-|-|\\(|\\))", "$1##4_DIGIT_NUMBER##$2");
	newContent = newContent.replaceAll("(\\s|^|-|\\-|\\(|\\))\\d{3}(\\s|$|\\-|-|\\(|\\))", "$1##3_DIGIT_NUMBER##$2");

	newContent = newContent.replaceAll("(\\s|^|-|\\-|\\(|\\))\\d{5}+(\\s|$|\\-|-|\\(|\\))", "$1##LONG_NUMBER##$2");
	newContent = newContent.replaceAll("(\\s|^|-|\\-|\\(|\\))\\d{4}(\\s|$|\\-|-|\\(|\\))", "$1##4_DIGIT_NUMBER##$2");
	newContent = newContent.replaceAll("(\\s|^|-|\\-|\\(|\\))\\d{3}(\\s|$|\\-|-|\\(|\\))", "$1##3_DIGIT_NUMBER##$2");
*/
	return newContent; 
    }
    
    /**
     * @param args
     */
    public static void main(String[] args)
    {
	if (args.length != 5)
	{
	    System.out.println("USAGE: ");
	    System.out.println("  DataExporter <class_option> <feature_option> <anno. id 1> <anno. id 2> <output file>");
	    System.out.println("");
	    System.out.println("Class Options:");
	    System.out.println("  1: function only");
	    System.out.println("  2: partial domain only");
	    System.out.println("  4: full domain only");
	    System.out.println("  3: function + partial domain");
	    System.out.println("  5: function + full domain");
	    
	    System.out.println("Feature Options:");
	    System.out.println("  1: previous function only");
	    System.out.println("  2: next function only");
	    System.out.println("  4: previous partial domain only");
	    System.out.println("  8: next partil domain only");
	    System.out.println("  16: previous full domain only"); 
	    System.out.println("  32: next full domain only");
	    return;
	}

	File file = new File(args[4]);

	int classOption = Integer.parseInt(args[0]);
	int featureOption = Integer.parseInt(args[1]);
	int anno1 = Integer.parseInt(args[2]);
	int anno2 = Integer.parseInt(args[3]);
	
	try
	{
	    file.createNewFile();
	    exportData(file, anno1, anno2, classOption, featureOption);
	} 
	catch (Exception e)
	{
	    System.out.println("Oops something is going wrong " + e); 
	    e.printStackTrace();
	} 
	finally
	{
	    System.out.println("Output written to " + file.getName());
	}
    }

    public static void exportData(File file, int anno1, int anno2, int classOptions, int featureOptions)
    throws FileNotFoundException, IOException 
    {
	if (file == null) {
	    throw new IllegalArgumentException("File should not be null.");
	}
	if (!file.exists()) {
	    throw new FileNotFoundException ("File does not exist: " + file);
	}
	if (!file.isFile()) {
	    throw new IllegalArgumentException("Should not be a directory: " + file);
	}
	if (!file.canWrite()) {
	    throw new IllegalArgumentException("File cannot be written: " + file);
	}

	//use buffering
	BufferedWriter output = new BufferedWriter(new FileWriter(file));

	AssignmentManager am = new AssignmentManager();
	List<Assignment> assignments = am.getAllCompletedAssignments(anno1, anno2);

	DialogueActDimensionManager dadm = new DialogueActDimensionManager();
	DialogueActManager dam = new DialogueActManager();
	DialogueActLabelManager dalm = new DialogueActLabelManager();    

	String nowString =  new Date().toString().replace(' ', '_');

	//
	// Start output the data
	//
	
	// Starting from comments...
	//
	output.write("%");
	output.newLine();

	
	// Generating class label definitions
	// 
	String classLabels = "";

	DialogueActDimension function = dadm.getDialogueActDimension("Function");
	Collection <DialogueActLabel> functionDALabels = dalm.getDialogueActLabelsNoTypo(function);
	DialogueActDimension domain = dadm.getDialogueActDimension("Domain");
	Collection <DialogueActLabel> domainDALabels = dalm.getDialogueActLabels(domain);

	if ((classOptions & FUNCTION) != 0)
	{
	    if ((classOptions & DOMAIN_PART) != 0)
	    {
		for (DialogueActLabel functionDALabel: functionDALabels) 
		{
		    for (DialogueActLabel domainDALabel: domainDALabels) 
		    {
			if (! classLabels.equals(""))
			    classLabels += ", ";
			classLabels += functionDALabel.getLabel().replace(' ', '_') + domainDALabel.getPartLabel().replace(' ', '_');
		    }	    	    
		}		
	    } 
	    else if ((classOptions & DOMAIN_FULL) != 0)
	    {
		for (DialogueActLabel functionDALabel: functionDALabels) 
		{
		    for (DialogueActLabel domainDALabel: domainDALabels) 
		    {
			if (! classLabels.equals(""))
			    classLabels += ", ";
			classLabels += functionDALabel.getLabel().replace(' ', '_') + domainDALabel.getLabel().replace(' ', '_');
		    }	    	    
		}		
	    } 
	    else 
	    {
		for (DialogueActLabel functionDALabel: functionDALabels) 
		{
		    if (! classLabels.equals(""))
			classLabels += ", ";
		    classLabels += functionDALabel.getLabel().replace(' ', '_');
		}
	    }
	}
	else if ((classOptions & DOMAIN_PART) != 0)
	{
	    for (DialogueActLabel domainDALabel: domainDALabels) 
	    {
		if (! classLabels.equals(""))
		    classLabels += ", ";
		classLabels += domainDALabel.getPartLabel().replace(' ', '_');
	    }	    	    
	} 
	else if ((classOptions & DOMAIN_FULL) != 0)
	{
	    for (DialogueActLabel domainDALabel: domainDALabels) 
	    {
		if (! classLabels.equals(""))
		    classLabels += ", ";
		classLabels += domainDALabel.getLabel().replace(' ', '_');
	    }		
	} 

	//
	// Generating feature definitions 
	//
	String functions = "";    // needs to be set !!!	
	String domainsFull = ""; // needs to be set !!
	String domainsPart = ""; // neneds to be set!!

	if ((featureOptions & (PREV_FUNCTION | NEXT_FUNCTION)) != 0)
	{
	    for (DialogueActLabel daLabel: functionDALabels)
	    {
		if (! functions.equals(""))
		    functions += ", ";
		functions += daLabel.getLabel();
	    }	    	    
	}

	if ((featureOptions & (PREV_DOMAIN_FULL | NEXT_DOMAIN_FULL)) != 0)
	{
	    for (DialogueActLabel daLabel: domainDALabels)
	    {
		if (! domainsFull.equals(""))
		    domainsFull += ", ";
		domainsFull += daLabel.getLabel();
	    }	    	    
	}

	if ((featureOptions & (PREV_DOMAIN_PART | NEXT_DOMAIN_PART)) != 0)
	{
	    for (DialogueActLabel daLabel: domainDALabels)
	    {
		if (! domainsPart.equals(""))
		    domainsPart += ", ";
		domainsPart += daLabel.getPartLabel();
	    }	    	    
	}

	int numConversations = assignments.size();
	int numMessages = 0;
	int numDialogueActs = 0;

	for (Assignment assignment: assignments)
	{
	    for (Message message: assignment.getConversation().getMessages())
	    {
		numMessages++;
		for (DialogueAct dialogueAct: message.getDialogueActs())
		{
		    if(assignment.getUser().getUserId() == dialogueAct.getUser().getUserId())
			numDialogueActs++;
		}
	    }
	}

	output.write("% ARFF file generated by Conversation Annotation Environment");
	output.newLine();
	output.write("% " + nowString);
	output.newLine();
	output.write("% Number of conversations: " + numConversations);
	output.newLine();				
	output.write("% Number of messages: " + numMessages);
	output.newLine();
	output.write("% Number of dialogue acts: " + numDialogueActs);
	output.newLine();
	output.write("%");
	output.newLine();
	output.write("@relation conversation_" + nowString);
	output.newLine();

	
	output.write("@attribute attr_dialogue_act {" + classLabels + "}");
	    output.newLine();
	
	if ((featureOptions & PREV_FUNCTION) != 0)  
	{
	    output.write("@attribute attr_prev_function {" + functions + ", START1 }");
	    output.newLine();
	}

	if ((featureOptions & NEXT_FUNCTION) != 0)  
	{
	    output.write("@attribute attr_next_function {" + functions + ", END1 }");
	    output.newLine();
	}


	if ((featureOptions & PREV_DOMAIN_PART) != 0)  
	{
	    output.write("@attribute attr_prev_domain {"  + domainsPart + ", START1 }");
	    output.newLine();
	}

	if ((featureOptions & NEXT_DOMAIN_PART) != 0)  
	{
	    output.write("@attribute attr_next_domain {" + domainsPart + ", END1 }");
	    output.newLine();
	}


	if ((featureOptions & PREV_DOMAIN_FULL) != 0)  
	{
	    output.write("@attribute attr_prev_domain {"  + domainsFull + ", START1 }");
	    output.newLine();
	}

	if ((featureOptions & NEXT_DOMAIN_FULL) != 0)   
	{
	    output.write("@attribute attr_next_domain {" + domainsFull + ", END1 }");
	    output.newLine();
	}

	output.write("@attribute attr_conversationId numeric");
	output.newLine();
	output.write("@attribute attr_messageId numeric");
	output.newLine();
	output.write("@attribute attr_sequence numeric");
	output.newLine();
	output.write("@attribute attr_message_length numeric");
	output.newLine();	
	output.write("@attribute attr_start_positon numeric");
	output.newLine();
	output.write("@attribute attr_end_position numeric");
	output.newLine();
	output.write("@attribute attr_userId numeric");
	output.newLine();
	output.write("@attribute attr_speaker {1, 2}");
	output.newLine();

	output.write("@attribute attr_text string");
	output.newLine();

	output.write("@data");
	output.newLine();

	HashSet<String> segmentsDone = new HashSet<String>();
	
	for (Assignment assignment: assignments)
	{
	    User user = assignment.getUser();
	    
	    
	    for (Message message: assignment.getConversation().getMessages())
	    {
		for (DialogueAct dialogueAct: message.getDialogueActs(user))
		{
		    // For now, I am just adding the first Dialogue Act
		    // it shouldn't matter much as long as it is only about shallow domain or function only
		    //
		    // List<DialogueAct> das = dam.getDialogueActsBySegement(user, message, dialogueAct.getStartIndex(), dialogueAct.getEndIndex());
		    	
		    String segment = dialogueAct.getUser().getUserId() + ":" +
		    			dialogueAct.getMessage().getMessageId() + ":" +
		    			dialogueAct.getStartIndex() + ":" +
		    			dialogueAct.getEndIndex();
		    
		    if(! dialogueAct.getFunctionLabel().equals("Log:Disconnect") && 
			    ! dialogueAct.getFunctionLabel().equals("Uninterpretable") && 
			    ! segmentsDone.contains(segment))
		    {
			segmentsDone.add(segment);
			
			String classLabel = "";

			if ((classOptions & FUNCTION) > 0)
			    classLabel += dialogueAct.getFunctionLabel();
			if ((classOptions & DOMAIN_PART) > 0) 
			    classLabel += dialogueAct.getPartDomainLabel();
			if ((classOptions & DOMAIN_FULL) > 0)   
			    classLabel += dialogueAct.getDomainLabel();

			output.write(classLabel + ", ");

			if ((featureOptions & PREV_FUNCTION) > 0)  
			    output.write(dialogueAct.getPreviousDialogueActFunctionLabel() + ", ");
			if ((featureOptions & NEXT_FUNCTION) > 0)  
			    output.write(dialogueAct.getNextDialogueActFunctionLabel() + ", ");
			if ((featureOptions & PREV_DOMAIN_PART) > 0)  
			    output.write(dialogueAct.getPreviousDialogueActPartDomainLabel() + ", ");
			if ((featureOptions & NEXT_DOMAIN_PART) > 0)   
			    output.write(dialogueAct.getNextDialogueActPartDomainLabel()  + ", ");
			if ((featureOptions & PREV_DOMAIN_FULL) > 0)  
			    output.write(dialogueAct.getPreviousDialogueActDomainLabel()  + ", ");
			if ((featureOptions & NEXT_DOMAIN_FULL) > 0)  
			    output.write(dialogueAct.getNextDialogueActDomainLabel()  + ", ");

			output.write(assignment.getConversation().getConversationId()  + ", ");
			output.write(dialogueAct.getMessage().getMessageId()  + ", ");
			output.write(dialogueAct.getMessage().getSequence()  + ", ");
			output.write(dialogueAct.getMessage().getNumberOfWords()  + ", ");
			output.write(dialogueAct.getStartIndex()  + ", ");
			output.write(dialogueAct.getEndIndex() + ", ");
			output.write(dialogueAct.getUser().getUserId()  + ", ");
			output.write(dialogueAct.getMessage().getSpeaker()  + ", ");
			output.write("\"" + filter(dialogueAct.getTextSegment()) + "\"");
			output.newLine();
		    }
		}
	    }
	    // BufferedWriter's default buffer size is 8192 characters. So flush it after each assignment.
	    //
	    output.flush();  
	}
    }    
    /**
     * Change the contents of text file in its entirety, overwriting any
     * existing text.
     *
     * This style of implementation throws all exceptions to the caller.
     *
     * @param aFile is an existing file which can be written to.
     * @throws IllegalArgumentException if param does not comply.
     * @throws FileNotFoundException if the file does not exist.
     * @throws IOException if problem encountered during write.
     */
    static public void setContents(File aFile, String aContents)
    throws FileNotFoundException, IOException 
    {	
	if (aFile == null) {
	    throw new IllegalArgumentException("File should not be null.");
	}
	if (!aFile.exists()) {
	    throw new FileNotFoundException ("File does not exist: " + aFile);
	}
	if (!aFile.isFile()) {
	    throw new IllegalArgumentException("Should not be a directory: " + aFile);
	}
	if (!aFile.canWrite()) {
	    throw new IllegalArgumentException("File cannot be written: " + aFile);
	}

	//use buffering
	Writer output = new BufferedWriter(new FileWriter(aFile));
	try {
	    //FileWriter always assumes default encoding is OK!
	    output.write( aContents );
	}
	finally {
	    output.close();	    
	}
    }
}
